﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Data;

namespace Liju.Common
{
    /// <summary>
    /// 位移编码器
    /// </summary>
    public class ModbusRTU
    {
        public static SerialPort serialPort;

        /// <summary>
        /// 位移编码器串口连接状态 -1异常 0未连接 1已连接
        /// </summary>
        public static int SerialPortState = 0;

        /// <summary>
        /// 位移编码器串口号名称
        /// </summary>
        public static string PortName = string.Empty;

        public ModbusRTU()
        {
            try
            {
                if (serialPort == null && !string.IsNullOrWhiteSpace(PortName))
                {
                    serialPort = new SerialPort(PortName, 115200, Parity.None, 8, StopBits.One);
                    serialPort.ReadTimeout = 1000; // 读取超时时间
                    serialPort.WriteTimeout = 1000; // 写入超时时间
                    serialPort.Open(); // 打开串口
                    SerialPortState = 1;
                }
                if (serialPort != null &&  !serialPort.IsOpen)
                {

                }
            }
            catch
            {
                SerialPortState = -1;
            }
        }

        // 构造函数，初始化串口参数
        public ModbusRTU(string portName, int baudRate, int dataBits, Parity parity, StopBits stopBits)
        {
            serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
            serialPort.ReadTimeout = 1000; // 读取超时时间
            serialPort.WriteTimeout = 1000; // 写入超时时间
            serialPort.Open(); // 打开串口
        }

        /// <summary>
        /// 初始化串口
        /// </summary>
        /// <param name="portName"></param>
        public ModbusRTU(string portName)
        {
            try
            {
                if (serialPort == null)
                {
                    serialPort = new SerialPort(portName, 115200, Parity.None, 8, StopBits.One);
                    serialPort.ReadTimeout = 1000; // 读取超时时间
                    serialPort.WriteTimeout = 1000; // 写入超时时间
                    serialPort.Open(); // 打开串口
                    SerialPortState = 1;
                }
                if (serialPort != null && !serialPort.IsOpen)
                {

                }
            }
            catch
            {
                SerialPortState = -1;
            }
        }

        /// <summary>
        /// 第一次或修改位移编码器端口设置 初始化端口
        /// </summary>
        /// <param name="bIsFirst"></param>
        public static void CheckSerialPortStae(bool bIsFirst)
        {
            //if (serialPort == null || bIsFirst)//未初始化或未打开 则打开
            {
                ////设备通讯 
                //DataTable dtComSet = Bll.GetComPortList();

                //ModbusRTU();
            }
        }

        /// <summary>
        /// 设置当前位置归零
        /// </summary>
        /// <param name="slaveAddress">从站号</param>
        public void ResetEncoderZeroPoint(byte slaveAddress)
        {
            // 构造发送帧
            byte[] frame = new byte[8];
            frame[0] = slaveAddress; // 从站地址
            frame[1] = 0x06; // 功能码 (06 写单个寄存器)
            frame[2] = 0x00; // 寄存器地址高字节 (0x0008)
            frame[3] = 0x08; // 寄存器地址低字节 (0x0008)
            frame[4] = 0x00; // 数据高字节 (0x0001)
            frame[5] = 0x01; // 数据低字节 (0x0001)

            ushort crc = CalculateCRC(frame, 6); // 计算CRC
            frame[6] = (byte)(crc & 0xFF); // CRC低字节
            frame[7] = (byte)((crc >> 8) & 0xFF); // CRC高字节

            // 发送帧到从站
            serialPort.Write(frame, 0, frame.Length);
            Thread.Sleep(50);
            // 接收响应帧
            byte[] response = new byte[8];
            if (serialPort.IsOpen == false)
            {
                serialPort.Open();
            }
            serialPort.Read(response, 0, response.Length);

            // 校验响应帧
            if (VerifyResponse(response, frame))
            {
                Console.WriteLine("编码器零点已重置");
            }
            else
            {
                Console.WriteLine("响应校验失败");
            }
        }

        // 读取保持寄存器方法
        public byte[] ReadHoldingRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
        {
            byte[] frame = CreateReadHoldingRegistersFrame(slaveAddress, startAddress, numberOfPoints);
            if(serialPort.IsOpen==false)
            {
                serialPort.Open();
            }
            serialPort.Write(frame, 0, frame.Length); // 发送请求帧
            Thread.Sleep(50); // 等待响应

            byte[] buffer = new byte[256];

            
            int bytesRead = serialPort.Read(buffer, 0, buffer.Length); // 读取响应

            if (bytesRead > 0)
            {
                byte[] response = new byte[bytesRead];
                Array.Copy(buffer, 0, response, 0, bytesRead); // 复制响应数据

                return response;
            }
            else
            {
                throw new Exception("No response from the device."); // 没有响应抛出异常
            }
        }

        public int GetFrameNum(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
        {
            byte[] frame = CreateReadHoldingRegistersFrame(slaveAddress, startAddress, numberOfPoints);
            if (serialPort.IsOpen == false)
            {
                serialPort.Open();
            }
            serialPort.Write(frame, 0, frame.Length); // 发送请求帧
            Thread.Sleep(50); // 等待响应

            byte[] buffer = new byte[256];
            try
            {
                int bytesRead = serialPort.Read(buffer, 0, buffer.Length); // 读取响应

                if (bytesRead > 0)
                {
                    byte[] response = new byte[bytesRead];
                    Array.Copy(buffer, 0, response, 0, bytesRead); // 复制响应数据

                    string[] hexValues = BitConverter.ToString(response).Split('-');
                    if (hexValues.Length < 5)
                    {
                        ////serialPort.DiscardInBuffer();
                        //serialPort.Close();
                        //Thread.Sleep(20); // 等待响应
                        //serialPort.Open();
                        //int nReturn = GetFrameNum(slaveAddress, startAddress, numberOfPoints);
                        //return nReturn;
                        return -1;
                    }
                    // 解析编码器单圈值的高字节和低字节
                    byte highByte = Convert.ToByte(hexValues[3], 16);
                    byte lowByte = Convert.ToByte(hexValues[4], 16);

                    // 将两个字节合并为一个16位整数
                    int encoderValue = (highByte << 8) | lowByte;

                    return encoderValue;
                }
                else
                {
                    throw new Exception("No response from the device."); // 没有响应抛出异常
                }
            }
            catch (Exception)
            {
                return -1;
            }
        }

        /// <summary>
        /// 单圈解析
        /// </summary>
        /// <param name="frame"></param>
        /// <returns></returns>
        public int ParseModbusFrame(string frame)
        {
            string[] hexValues = frame.Split('-');
            if (hexValues.Length < 5)
            {
                return -1;
            }
            // 解析编码器单圈值的高字节和低字节
            byte highByte = Convert.ToByte(hexValues[3], 16);
            byte lowByte = Convert.ToByte(hexValues[4], 16);

            // 将两个字节合并为一个16位整数
            int encoderValue = (highByte << 8) | lowByte;

            return encoderValue;
        }

        /// <summary>
        /// 多圈解析
        /// </summary>
        /// <param name="frame"></param>
        /// <returns></returns>
        public int ParseModbusFrames(string frame)
        {
            string[] hexValues = frame.Split('-');
            byte[] byteArray = new byte[hexValues.Length];

            for (int i = 0; i < hexValues.Length; i++)
            {
                byteArray[i] = Convert.ToByte(hexValues[i], 16);
            }

            byte slaveAddress = byteArray[0];
            if (byteArray.Length < 6)
            {
			                        ////serialPort.DiscardInBuffer();
                        //serialPort.Close();
                        //Thread.Sleep(20); // 等待响应
                        //serialPort.Open();
                        //int nReturn = GetFrameNum(slaveAddress, startAddress, numberOfPoints);
                        //return nReturn;
                return -1;
            }
            byte functionCode = byteArray[1];
            byte byteCount = byteArray[2];

            Console.WriteLine($"从站地址: {slaveAddress}");
            Console.WriteLine($"功能码: {functionCode}");
            Console.WriteLine($"字节数: {byteCount}");

            // 解析数据区
            if (byteCount != 4)
            {
                Console.WriteLine("字节数与数据长度不匹配");

            }

            // 组合寄存器1和寄存器2为32位整数
            uint combinedValue = (uint)((byteArray[3] << 24) | (byteArray[4] << 16) | (byteArray[5] << 8) | byteArray[6]);
            return (int)combinedValue;
        }

        // 创建读取保持寄存器的请求帧
        private  byte[] CreateReadHoldingRegistersFrame(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
        {
            byte functionCode = 0x03; // 功能码 03
            byte[] frame = new byte[8];
            frame[0] = slaveAddress;
            frame[1] = functionCode;
            frame[2] = (byte)(startAddress >> 8);
            frame[3] = (byte)(startAddress & 0xFF);
            frame[4] = (byte)(numberOfPoints >> 8);
            frame[5] = (byte)(numberOfPoints & 0xFF);

            // 计算 CRC 校验码
            ushort crc = CalculateCRC(frame, 6);
            frame[6] = (byte)(crc & 0xFF);
            frame[7] = (byte)(crc >> 8);

            return frame;
        }

        // 计算 CRC 校验码的方法
        private  ushort CalculateCRC(byte[] data, int length)
        {
            ushort crc = 0xFFFF;
            for (int i = 0; i < length; i++)
            {
                crc ^= data[i];
                for (int j = 0; j < 8; j++)
                {
                    if ((crc & 0x0001) != 0)
                    {
                        crc >>= 1;
                        crc ^= 0xA001;
                    }
                    else
                    {
                        crc >>= 1;
                    }
                }
            }
            return crc;
        }

        private bool VerifyResponse(byte[] response, byte[] frame)
        {
            // 校验从站地址、功能码、寄存器地址、数据和CRC
            if (response.Length != 8) return false;
            for (int i = 0; i < 6; i++)
            {
                if (response[i] != frame[i]) return false;
            }

            ushort receivedCrc = (ushort)((response[7] << 8) | response[6]);
            ushort calculatedCrc = CalculateCRC(response, 6);

            return receivedCrc == calculatedCrc;
        }

        // 析构函数，关闭串口
        ~ModbusRTU()
        {
            if (serialPort != null && serialPort.IsOpen)
            {
                serialPort.Close();
            }
        }
    }
}
